import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from typing import Dict, List, Optional
import xml.etree.ElementTree as ET
import os
import threading
import time
import queue
from ctypes import c_char_p

from zlgcan import *


class TestResultWindow:
    """测试结果记录窗口"""

    def __init__(self, master):
        self.master = master
        self.window = tk.Toplevel(master)
        self.window.title("测试结果记录")
        self.window.geometry("600x400")
        self.window.protocol("WM_DELETE_WINDOW", self.hide_window)

        # 初始时隐藏窗口
        self.window.withdraw()

        # 创建主框架
        main_frame = tk.Frame(self.window)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)

        # 创建Treeview来显示测试结果
        columns = ("status", "timestamp", "details")
        self.tree = ttk.Treeview(main_frame, columns=columns, show="tree headings")

        # 设置列标题
        self.tree.heading("#0", text="测试用例", anchor="w")
        self.tree.heading("status", text="状态", anchor="center")
        self.tree.heading("timestamp", text="时间", anchor="center")
        self.tree.heading("details", text="详细信息", anchor="w")

        # 设置列宽
        self.tree.column("#0", width=200, minwidth=150)
        self.tree.column("status", width=80, minwidth=80, anchor="center")
        self.tree.column("timestamp", width=120, minwidth=100, anchor="center")
        self.tree.column("details", width=200, minwidth=150)

        # 添加滚动条
        scrollbar = ttk.Scrollbar(main_frame, orient="vertical", command=self.tree.yview)
        self.tree.configure(yscrollcommand=scrollbar.set)

        # 布局
        self.tree.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # 添加控制按钮框架
        button_frame = tk.Frame(self.window)
        button_frame.pack(fill="x", padx=10, pady=5)

        self.btn_clear = tk.Button(button_frame, text="清空记录", command=self.clear_results, width=10)
        self.btn_clear.pack(side="left", padx=5)

        self.btn_export = tk.Button(button_frame, text="导出结果", command=self.export_results, width=10)
        self.btn_export.pack(side="left", padx=5)

        self.btn_close = tk.Button(button_frame, text="关闭窗口", command=self.hide_window, width=10)
        self.btn_close.pack(side="right", padx=5)

        # 状态统计标签
        self.stats_label = tk.Label(self.window, text="总计: 0 | 成功: 0 | 失败: 0 | 进行中: 0",
                                    font=("Arial", 10, "bold"))
        self.stats_label.pack(fill="x", padx=10, pady=5)

        # 存储测试结果和统计信息
        self.test_results = {}
        self.window_visible = False
        self.stats = {"total": 0, "success": 0, "failure": 0, "running": 0}

        # 配置标签样式
        self.configure_styles()

    def configure_styles(self):
        """配置样式"""
        self.tree.tag_configure("success", background="#DFF0D8", foreground="#3C763D")
        self.tree.tag_configure("failure", background="#F2DEDE", foreground="#A94442")
        self.tree.tag_configure("running", background="#D9EDF7", foreground="#31708F")
        self.tree.tag_configure("warning", background="#FCF8E3", foreground="#8A6D3B")

    def add_test_result(self, test_name, status, details=""):
        """添加测试结果"""
        for item in self.tree.get_children():
            if self.tree.item(item, "text") == test_name:
                self.tree.delete(item)
                break

        timestamp = time.strftime("%H:%M:%S")
        item_id = self.tree.insert("", "end", text=test_name,
                                   values=(status, timestamp, details))

        if status == "成功":
            self.tree.item(item_id, tags=("success",))
            self.stats["success"] += 1
            self.stats["running"] = max(0, self.stats["running"] - 1)
        elif status == "失败":
            self.tree.item(item_id, tags=("failure",))
            self.stats["failure"] += 1
            self.stats["running"] = max(0, self.stats["running"] - 1)
        elif status == "进行中":
            self.tree.item(item_id, tags=("running",))
            self.stats["running"] += 1
            self.stats["total"] += 1
        else:
            self.tree.item(item_id, tags=("warning",))

        self.update_stats()
        self.tree.see(item_id)

        self.test_results[test_name] = {
            "status": status,
            "details": details,
            "timestamp": timestamp
        }

    def update_test_result(self, test_name, status, details=""):
        """更新测试结果"""
        self.add_test_result(test_name, status, details)

    def update_stats(self):
        """更新统计信息"""
        text = f"总计: {self.stats['total']} | 成功: {self.stats['success']} | 失败: {self.stats['failure']} | 进行中: {self.stats['running']}"
        self.stats_label.config(text=text)

    def clear_results(self):
        """清空所有测试结果"""
        for item in self.tree.get_children():
            self.tree.delete(item)
        self.test_results.clear()
        self.stats = {"total": 0, "success": 0, "failure": 0, "running": 0}
        self.update_stats()

    def export_results(self):
        """导出测试结果到文件"""
        filename = filedialog.asksaveasfilename(
            title="导出测试结果",
            defaultextension=".txt",
            filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
        )

        if filename:
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write("测试结果报告\n")
                    f.write("生成时间: {}\n".format(time.strftime("%Y-%m-%d %H:%M:%S")))
                    f.write("=" * 50 + "\n\n")

                    for test_name, result in self.test_results.items():
                        f.write("测试用例: {}\n".format(test_name))
                        f.write("状态: {}\n".format(result["status"]))
                        f.write("时间: {}\n".format(result["timestamp"]))
                        f.write("详情: {}\n".format(result["details"]))
                        f.write("-" * 30 + "\n")

                messagebox.showinfo("导出成功", f"测试结果已导出到: {filename}")
            except Exception as e:
                messagebox.showerror("导出失败", f"导出文件时出错: {e}")

    def show_window(self):
        """显示窗口"""
        if not self.window_visible:
            self.window.deiconify()
            self.window_visible = True
            self.window.lift()
            self.window.focus_force()

    def hide_window(self):
        """隐藏窗口"""
        if self.window_visible:
            self.window.withdraw()
            self.window_visible = False

    def start_test_session(self):
        """开始测试会话（显示窗口并清空旧结果）"""
        self.clear_results()
        self.show_window()


class CANMessage:
    def __init__(self):
        self.transmit_type = 0
        self.can_id = 0
        self.can_type = 0
        self.can_dlc = 0
        self.data = []
        self.pad = 0
        self.res0 = 10
        self.res1 = 0
        self.is_remote = False
        self.is_canfd = False

    def __str__(self):
        return (f"CANMessage(ID=0x{self.can_id:X}, Type={'EXT' if self.can_type == 1 else 'STD'}, "
                f"DLen={self.can_dlc}, Data={self.data}, "
                f"TransmitType={'自发自收' if self.transmit_type == 2 else '正常发送'}, "
                f"Remote={'是' if self.is_remote else '否'}, CANFD={'是' if self.is_canfd else '否'})")


class TestStep:
    """测试步骤类"""

    def __init__(self, step_config: Dict):
        self.sequence = int(step_config.get('sequence', 0))
        self.action = step_config.get('action', '')
        self.message_name = step_config.get('message_name', '')
        self.expected_message_name = step_config.get('expected_message_name', '')
        self.delay_ms = int(step_config.get('delay', 0))
        self.timeout_ms = int(step_config.get('timeout', 1000))
        self.on_success = step_config.get('on_success', {})
        self.on_failure = step_config.get('on_failure', {})

    def __str__(self):
        return f"Step {self.sequence}: {self.action} {self.message_name or self.expected_message_name}"


class TestScenario:
    """测试场景类"""

    def __init__(self, scenario_config: Dict):
        self.name = scenario_config.get('name', '')
        self.description = scenario_config.get('description', '')
        self.steps = []
        self.current_step_index = 0

    def add_step(self, step: TestStep):
        self.steps.append(step)
        self.steps.sort(key=lambda x: x.sequence)

    def get_next_step(self) -> Optional[TestStep]:
        if self.current_step_index < len(self.steps):
            step = self.steps[self.current_step_index]
            self.current_step_index += 1
            return step
        return None

    def reset(self):
        self.current_step_index = 0


class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.master.geometry("600x400")
        self.pack()
        self.create_widgets()

        self.device_opened = False
        self.device_handle = None
        self.can_number = 0
        self.handle = 0
        self.chn_handles = []
        self.threads = []
        self.enable_merge_receive = 0
        self.thread_flag = True
        self.print_lock = threading.Lock()

        # XML测试相关属性
        self.current_scenario = None
        self.message_definitions = {}
        self.is_test_running = False
        self.received_messages = []
        self.test_thread = None

        # 消息队列和事件
        self.received_messages_queue = queue.Queue()
        self.receive_event = threading.Event()

        # 创建测试结果窗口
        self.result_window = TestResultWindow(self.master)

        # 添加安全访问相关属性
        self.current_seed = None
        self.security_level = 1  # 默认安全级别
        self.is_unlocked = False


    def create_widgets(self):
        self.btn_OpenDev = tk.Button(self.master, text="打开设备", bg="#FFCC99", width=10, height=1)
        self.btn_OpenDev["command"] = self.open_device
        self.btn_OpenDev.place(x=10, y=10)

        self.btn_StartTest = tk.Button(self.master, text="开始测试", bg="#99CCFF", width=10, height=1)
        self.btn_StartTest["command"] = self.start_test
        self.btn_StartTest.place(x=100, y=10)

        self.btn_StopTest = tk.Button(self.master, text="停止测试", bg="#FF9999", width=10, height=1)
        self.btn_StopTest["command"] = self.stop_test
        self.btn_StopTest.place(x=190, y=10)

        self.btn_ShowResults = tk.Button(self.master, text="查看结果", bg="#CCCCFF", width=10, height=1,
                                         command=self.show_results, state="disabled")
        self.btn_ShowResults.place(x=280, y=10)

        self.lbl_XmlFile = tk.Label(self.master, text="XML配置文件:")
        self.lbl_XmlFile.place(x=10, y=50)

        self.entry_XmlFile = tk.Entry(self.master, width=30)
        self.entry_XmlFile.insert(0, "can_test_sequence.xml")
        self.entry_XmlFile.place(x=100, y=50)

        self.btn_BrowseXml = tk.Button(self.master, text="浏览", width=5, height=1)
        self.btn_BrowseXml["command"] = self.browse_xml_file
        self.btn_BrowseXml.place(x=300, y=45)

        self.lbl_Scenario = tk.Label(self.master, text="测试场景:")
        self.lbl_Scenario.place(x=10, y=80)

        self.scenario_var = tk.StringVar()
        self.combo_Scenario = tk.OptionMenu(self.master, self.scenario_var, "")
        self.combo_Scenario.place(x=100, y=80, width=150)

        self.lbl_Status = tk.Label(self.master, text="状态: 未开始", fg="blue")
        self.lbl_Status.place(x=10, y=110)

        self.text_Log = tk.Text(self.master, width=70, height=15)
        self.text_Log.place(x=10, y=140)

        self.scrollbar = tk.Scrollbar(self.master, orient="vertical", command=self.text_Log.yview)
        self.scrollbar.place(x=580, y=140, height=240)
        self.text_Log.config(yscrollcommand=self.scrollbar.set)

        self.btn_LoadXml = tk.Button(self.master, text="加载XML", bg="#CCFFCC", width=10, height=1)
        self.btn_LoadXml["command"] = self.load_xml_config
        self.btn_LoadXml.place(x=350, y=45)

        # 添加批量执行按钮
        self.btn_RunAll = tk.Button(self.master, text="批量执行", bg="#66CCFF", width=10, height=1,
                                    command=self.execute_all_test_scenarios)
        self.btn_RunAll.place(x=300, y=80)
        # 添加停止批量测试按钮
        self.btn_StopAll = tk.Button(self.master, text="停止批量", bg="#FF9999", width=10, height=1,
                                     command=self.stop_all_tests)
        self.btn_StopAll.place(x=400, y=80)

    def stop_all_tests(self):
        """停止所有测试"""
        self.is_test_running = False
        self.lbl_Status.config(text="状态: 已停止", fg="red")
        self.log_message("批量测试已停止")




    def show_results(self):
        """查看测试结果"""
        self.result_window.show_window()

    def browse_xml_file(self):
        filename = filedialog.askopenfilename(
            title="选择XML配置文件",
            filetypes=[("XML files", "*.xml"), ("All files", "*.*")]
        )
        if filename:
            self.entry_XmlFile.delete(0, tk.END)
            self.entry_XmlFile.insert(0, filename)

    def log_message(self, message):
        self.text_Log.insert(tk.END, f"{time.strftime('%H:%M:%S')} - {message}\n")
        self.text_Log.see(tk.END)

    def parse_test_scenario_from_xml(self, xml_file_path: str, scenario_name: str = None):
        if not os.path.exists(xml_file_path):
            raise FileNotFoundError(f"XML文件不存在: {xml_file_path}")

        try:
            tree = ET.parse(xml_file_path)
            root = tree.getroot()

            message_definitions = {}
            msg_defs_elem = root.find('MessageDefinitions')
            if msg_defs_elem is not None:
                for msg_elem in msg_defs_elem.findall('Message'):
                    name = msg_elem.get('name')
                    message_definitions[name] = {
                        'can_id': int(msg_elem.find('can_id').text, 0),
                        'can_type': int(msg_elem.find('can_type').text),
                        'data_array': [int(x.strip(), 0) for x in msg_elem.find('data_array').text.split(',')],
                        'transmit_type': int(msg_elem.find('transmit_type').text),
                        'frame_count': int(msg_elem.find('frame_count').text),
                        'is_canfd': msg_elem.find('is_canfd').text.lower() == 'true',
                        'is_remote': msg_elem.find('is_remote').text.lower() == 'true'
                    }

            scenarios = []
            for scenario_elem in root.findall('TestScenario'):
                scenario = TestScenario({
                    'name': scenario_elem.get('name'),
                    'description': scenario_elem.find('Description').text if scenario_elem.find(
                        'Description') is not None else ''
                })

                for step_elem in scenario_elem.findall('Step'):
                    step_config = {
                        'sequence': int(step_elem.get('sequence')),
                        'action': step_elem.find('Action').text if step_elem.find('Action') is not None else '',
                        'message_name': step_elem.find('Message').get('name') if step_elem.find(
                            'Message') is not None else '',
                        'expected_message_name': step_elem.find('ExpectedMessage').get('name') if step_elem.find(
                            'ExpectedMessage') is not None else '',
                        'delay': step_elem.find('Delay').text if step_elem.find('Delay') is not None else '0',
                        'timeout': step_elem.find('Timeout').text if step_elem.find('Timeout') is not None else '1000'
                    }

                    on_success = step_elem.find('OnSuccess')
                    if on_success is not None:
                        step_config['on_success'] = {
                            'next_step': on_success.find('NextStep').text if on_success.find(
                                'NextStep') is not None else None
                        }

                    on_failure = step_elem.find('OnFailure')
                    if on_failure is not None:
                        step_config['on_failure'] = {
                            'error_code': on_failure.find('ErrorCode').text if on_failure.find(
                                'ErrorCode') is not None else None,
                            'error_message': on_failure.find('ErrorMessage').text if on_failure.find(
                                'ErrorMessage') is not None else '',
                            'abort_test': on_failure.find('AbortTest').text.lower() == 'true' if on_failure.find(
                                'AbortTest') is not None else False
                        }

                    scenario.add_step(TestStep(step_config))

                scenarios.append(scenario)

            if not scenarios:
                raise ValueError("XML文件中未找到测试场景")

            if scenario_name:
                for scenario in scenarios:
                    if scenario.name == scenario_name:
                        return scenario, message_definitions
                raise ValueError(f"未找到名为 '{scenario_name}' 的测试场景")
            else:
                return scenarios[0], message_definitions

        except ET.ParseError as e:
            raise ValueError(f"XML文件解析错误: {e}")
        except AttributeError as e:
            raise ValueError(f"XML文件格式错误，缺少必要字段: {e}")

    def load_xml_config(self):
        """加载XML配置文件"""
        xml_file = self.entry_XmlFile.get().strip()
        if not xml_file:
            self.log_message("错误: 请指定XML文件路径")
            return

        try:
            # 获取所有场景名称
            tree = ET.parse(xml_file)
            root = tree.getroot()

            self.all_test_scenarios = []
            for scenario_elem in root.findall('TestScenario'):
                self.all_test_scenarios.append(scenario_elem.get('name'))

            # 按名称排序
            self.all_test_scenarios.sort()

            # 更新下拉菜单
            menu = self.combo_Scenario["menu"]
            menu.delete(0, "end")
            for name in self.all_test_scenarios:
                menu.add_command(label=name, command=lambda value=name: self.scenario_var.set(value))

            if self.all_test_scenarios:
                self.scenario_var.set(self.all_test_scenarios[0])
                self.log_message(f"成功加载XML文件，找到 {len(self.all_test_scenarios)} 个测试场景")

                # 启用批量执行按钮
                self.btn_RunAll.config(state="normal")
                self.btn_StopAll.config(state="normal")
            else:
                self.log_message("警告: XML文件中未找到测试场景")
                self.btn_RunAll.config(state="disabled")
                self.btn_StopAll.config(state="disabled")

        except Exception as e:
            self.log_message(f"加载XML文件失败: {e}")
            self.btn_RunAll.config(state="disabled")
            self.btn_StopAll.config(state="disabled")

    def calculate_security_key(self, seed):
        """
        计算安全密钥（示例算法，需要根据实际ECU算法实现）

        Args:
            seed: 从ECU接收到的种子

        Returns:
            int: 计算出的密钥
        """
        # 这里是一个示例算法，实际算法需要根据ECU厂商提供
        # 常见的算法有：移位、异或、查表等

        # 示例算法：简单的移位和异或
        key = seed
        key = (key << 3) & 0xFFFFFFFF  # 左移3位
        key = key ^ 0x12345678  # 异或固定值
        key = (key + 0xABCDEF) & 0xFFFFFFFF  # 加上固定值

        self.log_message(f"计算密钥: 种子=0x{seed:08X}, 密钥=0x{key:08X}")
        return key

    def perform_security_access(self, security_level=1):
        """
        执行安全访问解锁流程

        Args:
            security_level: 安全级别（通常1-3）

        Returns:
            bool: 是否解锁成功
        """
        try:
            self.log_message(f"开始安全访问解锁，级别: {security_level}")

            # 1. 请求种子
            seed_msg_name = f"uds_request_seed_{security_level:02d}"
            if seed_msg_name not in self.message_definitions:
                self.log_message(f"错误: 未找到安全级别 {security_level} 的种子请求消息")
                return False

            # 发送种子请求
            seed_config = self.message_definitions[seed_msg_name]
            self.set_canmsg(**seed_config)
            self.Transmit_msg(self.chn_handles[0])

            # 等待种子响应
            self.log_message("等待种子响应...")
            seed_response = self.wait_for_uds_response(0x67, security_level, timeout=2000)

            if not seed_response or len(seed_response['data']) < 6:
                self.log_message("错误: 未收到有效的种子响应")
                return False

            # 解析种子（通常在第4-7字节）
            seed = (seed_response['data'][3] << 24) | (seed_response['data'][4] << 16) | \
                   (seed_response['data'][5] << 8) | seed_response['data'][6]
            self.current_seed = seed
            self.log_message(f"收到种子: 0x{seed:08X}")

            # 2. 计算并发送密钥
            calculated_key = self.calculate_security_key(seed)

            # 创建密钥消息（需要动态设置密钥数据）
            key_msg_name = f"uds_send_key_{security_level:02d}"
            if key_msg_name not in self.message_definitions:
                self.log_message(f"错误: 未找到安全级别 {security_level} 的密钥发送消息")
                return False

            key_config = self.message_definitions[key_msg_name].copy()
            # 动态设置密钥数据（大端字节序）
            key_config['data_array'] = [
                0x27, 0x02,  # 服务ID和子功能
                (calculated_key >> 24) & 0xFF,  # 密钥字节1
                (calculated_key >> 16) & 0xFF,  # 密钥字节2
                (calculated_key >> 8) & 0xFF,  # 密钥字节3
                calculated_key & 0xFF,  # 密钥字节4
                0x00, 0x00  # 填充
            ]

            self.set_canmsg(**key_config)
            self.Transmit_msg(self.chn_handles[0])

            # 等待密钥响应
            self.log_message("发送密钥，等待响应...")
            key_response = self.wait_for_uds_response(0x67, security_level + 1, timeout=2000)

            if key_response and key_response['data'][2] == 0x02:  # 正响应，子功能=02
                self.is_unlocked = True
                self.log_message("安全访问解锁成功!")
                return True
            else:
                self.log_message("安全访问解锁失败")
                return False

        except Exception as e:
            self.log_message(f"安全访问执行异常: {e}")
            return False

    def wait_for_uds_response(self, expected_service, expected_subbunction, timeout=1000):
        """
        等待特定的UDS响应

        Args:
            expected_service: 期望的服务ID（如0x67）
            expected_subbunction: 期望的子功能
            timeout: 超时时间(ms)

        Returns:
            dict or None: 接收到的消息字典
        """
        start_time = time.time()
        timeout_sec = timeout / 1000.0

        while time.time() - start_time < timeout_sec:
            try:
                received_msg = self.received_messages_queue.get(timeout=0.1)

                # 检查是否是UDS响应（CAN ID = 0x7E8）
                if received_msg['can_id'] == 0x7E8 and len(received_msg['data']) >= 3:
                    service_id = received_msg['data'][0]
                    subfunction = received_msg['data'][1]

                    if service_id == expected_service and subfunction == expected_subbunction:
                        return received_msg

            except queue.Empty:
                if not self.is_test_running:
                    break
                continue

        return None

    def check_security_access(self):
        """
        检查当前安全访问状态
        """
        if self.is_unlocked:
            self.log_message("当前已处于解锁状态")
            return True
        else:
            self.log_message("当前未解锁，需要执行安全访问")
            return False

    def reset_security_access(self):
        """
        重置安全访问状态
        """
        self.is_unlocked = False
        self.current_seed = None
        self.log_message("安全访问状态已重置")

    def start_test(self):
        """开始执行单个测试（保持原有功能）"""
        if not self.device_opened:
            self.log_message("错误: 请先打开设备")
            return

        xml_file = self.entry_XmlFile.get().strip()
        scenario_name = self.scenario_var.get()

        if not xml_file or not scenario_name:
            self.log_message("错误: 请选择XML文件和测试场景")
            return

        try:
            self.current_scenario, self.message_definitions = self.parse_test_scenario_from_xml(
                xml_file, scenario_name
            )

            # 开始测试会话
            self.result_window.start_test_session()
            self.btn_ShowResults.config(state="normal")

            self.is_test_running = True
            self.current_scenario.reset()
            self.received_messages.clear()

            self.lbl_Status.config(text="状态: 测试中", fg="green")
            self.log_message(f"开始测试场景: {self.current_scenario.name}")

            # 记录测试开始
            self.result_window.add_test_result(
                self.current_scenario.name,
                "进行中",
                f"开始时间: {time.strftime('%H:%M:%S')}"
            )

            # 在单独线程中执行测试
            self.test_thread = threading.Thread(target=self.run_single_test)
            self.test_thread.daemon = True
            self.test_thread.start()

        except Exception as e:
            self.log_message(f"启动测试失败: {e}")
            self.result_window.add_test_result(
                "测试启动",
                "失败",
                f"启动失败: {e}"
            )

    def run_single_test(self):
        """运行单个测试（原有逻辑）"""
        test_success = True
        error_details = ""

        try:
            while self.is_test_running:
                step = self.current_scenario.get_next_step()
                if not step:
                    break

                self.log_message(f"执行步骤 {step.sequence}: {step.action}")

                try:
                    if step.action == 'send':
                        self.execute_send_step(step)
                    elif step.action == 'receive':
                        success = self.execute_receive_step(step)
                        if not success:
                            test_success = False
                            error_details = f"步骤 {step.sequence} 接收失败"
                            break  # 单个测试失败时停止
                    elif step.action == 'security_access':
                        success = self.perform_security_access(getattr(step, 'security_level', 1))
                        if not success:
                            test_success = False
                            error_details = f"步骤 {step.sequence} 安全访问失败"
                            break  # 单个测试失败时停止
                    else:
                        self.log_message(f"未知操作: {step.action}")

                except Exception as e:
                    self.log_message(f"步骤 {step.sequence} 执行失败: {e}")
                    test_success = False
                    error_details = f"步骤 {step.sequence} 执行异常: {e}"
                    break  # 单个测试失败时停止

            # 更新测试结果
            if test_success:
                self.result_window.update_test_result(
                    self.current_scenario.name,
                    "成功",
                    f"完成时间: {time.strftime('%H:%M:%S')}"
                )
                self.log_message("测试完成！")
                self.lbl_Status.config(text="状态: 完成", fg="blue")
            else:
                self.result_window.update_test_result(
                    self.current_scenario.name,
                    "失败",
                    error_details
                )
                self.log_message(f"测试失败: {error_details}")
                self.lbl_Status.config(text="状态: 失败", fg="red")

        except Exception as e:
            self.result_window.update_test_result(
                self.current_scenario.name if self.current_scenario else "未知测试",
                "异常",
                f"测试执行异常: {e}"
            )
            self.log_message(f"测试执行异常: {e}")
            self.lbl_Status.config(text="状态: 异常", fg="orange")

        self.is_test_running = False

    def stop_test(self):
        self.is_test_running = False
        self.lbl_Status.config(text="状态: 已停止", fg="red")
        self.log_message("测试已停止")

        if self.current_scenario:
            self.result_window.update_test_result(
                self.current_scenario.name,
                "已停止",
                "测试被用户手动停止"
            )

    def execute_all_test_scenarios(self):
        """执行所有测试场景，即使失败也继续"""
        if not hasattr(self, 'all_test_scenarios') or not self.all_test_scenarios:
            self.log_message("错误: 请先加载测试场景")
            return

        self.is_test_running = True
        self.current_test_index = 0
        self.lbl_Status.config(text="状态: 批量测试中", fg="green")

        # 清空之前的结果
        self.result_window.clear_results()
        self.result_window.show_window()

        # 开始执行第一个测试
        self.execute_next_test_scenario()

    def execute_next_test_scenario(self):
        """执行下一个测试场景"""
        if not self.is_test_running or self.current_test_index >= len(self.all_test_scenarios):
            self.is_test_running = False
            self.lbl_Status.config(text="状态: 批量测试完成", fg="blue")
            self.log_message("所有测试场景执行完成！")
            return

        test_name = self.all_test_scenarios[self.current_test_index]
        self.log_message(f"开始执行测试 {self.current_test_index + 1}/{len(self.all_test_scenarios)}: {test_name}")

        # 记录测试开始
        self.result_window.add_test_result(
            test_name,
            "进行中",
            f"开始时间: {time.strftime('%H:%M:%S')}"
        )

        # 执行单个测试场景
        self.execute_single_test_scenario(test_name)

    def execute_single_test_scenario(self, scenario_name):
        """执行单个测试场景"""
        try:
            # 加载测试场景
            self.current_scenario, self.message_definitions = self.parse_test_scenario_from_xml(
                self.entry_XmlFile.get().strip(), scenario_name
            )

            if not self.current_scenario:
                raise ValueError(f"未找到测试场景: {scenario_name}")

            self.current_scenario.reset()
            self.received_messages.clear()

            # 在单独线程中执行测试
            self.test_thread = threading.Thread(target=self.run_test_scenario, args=(scenario_name,))
            self.test_thread.daemon = True
            self.test_thread.start()

        except Exception as e:
            self.log_message(f"执行测试场景 {scenario_name} 失败: {e}")
            self.result_window.update_test_result(
                scenario_name,
                "失败",
                f"初始化失败: {e}"
            )
            # 继续执行下一个测试
            self.current_test_index += 1
            self.master.after(500, self.execute_next_test_scenario)  # 0.5秒后执行下一个

    def run_test_scenario(self, scenario_name):
        """运行单个测试场景"""
        test_success = True
        error_details = ""

        try:
            self.current_scenario.reset()

            while self.is_test_running:
                step = self.current_scenario.get_next_step()
                if not step:
                    break

                self.log_message(f"[{scenario_name}] 执行步骤 {step.sequence}: {step.action}")

                try:
                    if step.action == 'send':
                        self.execute_send_step(step)
                    elif step.action == 'receive':
                        success = self.execute_receive_step(step)
                        if not success:
                            test_success = False
                            error_details = f"步骤 {step.sequence} 接收失败"
                            # 不break，继续执行剩余步骤
                    elif step.action == 'security_access':
                        success = self.perform_security_access(getattr(step, 'security_level', 1))
                        if not success:
                            test_success = False
                            error_details = f"步骤 {step.sequence} 安全访问失败"
                            # 不break，继续执行剩余步骤
                    else:
                        self.log_message(f"未知操作: {step.action}")

                except Exception as e:
                    self.log_message(f"步骤 {step.sequence} 执行失败: {e}")
                    test_success = False
                    error_details = f"步骤 {step.sequence} 执行异常: {e}"
                    # 继续执行下一个步骤

            # 更新测试结果
            if test_success:
                self.result_window.update_test_result(
                    scenario_name,
                    "成功",
                    f"完成时间: {time.strftime('%H:%M:%S')}"
                )
                self.log_message(f"[{scenario_name}] 测试完成！")
            else:
                self.result_window.update_test_result(
                    scenario_name,
                    "失败",
                    error_details
                )
                self.log_message(f"[{scenario_name}] 测试失败: {error_details}")

        except Exception as e:
            self.result_window.update_test_result(
                scenario_name,
                "异常",
                f"测试执行异常: {e}"
            )
            self.log_message(f"[{scenario_name}] 测试执行异常: {e}")

        # 执行完成后，继续下一个测试
        self.current_test_index += 1
        self.master.after(500, self.execute_next_test_scenario)  # 0.5秒后执行下一个


    def execute_test_sequence(self):
        """执行测试序列"""
        test_success = True
        error_details = ""

        try:
            while self.is_test_running:
                step = self.current_scenario.get_next_step()
                if not step:
                    break

                self.log_message(f"执行步骤 {step.sequence}: {step.action}")

                try:
                    if step.action == 'send':
                        self.execute_send_step(step)
                    elif step.action == 'receive':
                        success = self.execute_receive_step(step)
                        if not success:
                            test_success = False
                            error_details = f"步骤 {step.sequence} 接收失败"
                            break
                    elif step.action == 'security_access':  # 新增安全访问操作
                        success = self.perform_security_access(step.security_level)
                        if not success:
                            test_success = False
                            error_details = f"步骤 {step.sequence} 安全访问失败"
                            break
                    else:
                        self.log_message(f"未知操作: {step.action}")

                except Exception as e:
                    self.log_message(f"步骤 {step.sequence} 执行失败: {e}")
                    test_success = False
                    error_details = f"步骤 {step.sequence} 执行异常: {e}"
                    break

            # 更新测试结果
            if test_success:
                self.result_window.update_test_result(
                    self.current_scenario.name,
                    "成功",
                    f"完成时间: {time.strftime('%H:%M:%S')}"
                )
            else:
                self.result_window.update_test_result(
                    self.current_scenario.name,
                    "失败",
                    error_details
                )

        except Exception as e:
            self.result_window.update_test_result(
                self.current_scenario.name if self.current_scenario else "未知测试",
                "异常",
                f"测试执行异常: {e}"
            )

        self.is_test_running = False

    def execute_send_step(self, step: TestStep):
        if step.message_name not in self.message_definitions:
            raise ValueError(f"未找到消息定义 '{step.message_name}'")

        msg_config = self.message_definitions[step.message_name]
        self.set_canmsg(**msg_config)

        if self.chn_handles:
            self.Transmit_msg(self.chn_handles[0])
            self.log_message(f"已发送消息: {step.message_name}")
        else:
            raise ValueError("没有可用的通道句柄")

        if step.delay_ms > 0:
            self.log_message(f"等待 {step.delay_ms}ms...")
            time.sleep(step.delay_ms / 1000.0)

    def execute_receive_step(self, step: TestStep) -> bool:
        if step.expected_message_name not in self.message_definitions:
            raise ValueError(f"未找到期望消息定义 '{step.expected_message_name}'")

        expected_msg = self.message_definitions[step.expected_message_name]
        self.log_message(f"等待接收消息: {step.expected_message_name}, 超时: {step.timeout_ms}ms")

        # 清空之前的消息
        while not self.received_messages_queue.empty():
            try:
                self.received_messages_queue.get_nowait()
            except queue.Empty:
                break

        # 设置接收事件
        self.receive_event.set()

        # 等待期望的消息
        received = self.wait_for_expected_message(expected_msg, step.timeout_ms)

        # 清除接收事件
        self.receive_event.clear()

        return received

    def wait_for_expected_message(self, expected_msg: Dict, timeout_ms: int) -> bool:
        start_time = time.time()
        timeout_sec = timeout_ms / 1000.0

        while time.time() - start_time < timeout_sec:
            try:
                received_msg = self.received_messages_queue.get(timeout=0.1)
                if self.compare_messages(received_msg, expected_msg):
                    return True
            except queue.Empty:
                if not self.is_test_running:
                    break
                continue
        return False

    def compare_messages(self, actual_msg, expected_msg: Dict) -> bool:
        if actual_msg['can_id'] != expected_msg['can_id']:
            return False
        if actual_msg['can_type'] != expected_msg['can_type']:
            return False
        if len(actual_msg['data']) != len(expected_msg['data_array']):
            return False
        for i in range(len(actual_msg['data'])):
            if actual_msg['data'][i] != expected_msg['data_array'][i]:
                return False
        return True

    def set_canmsg(self, can_id, can_type, data_array, transmit_type=2, frame_count=1, is_canfd=False, is_remote=False):
        if not isinstance(can_id, int) or can_id < 0:
            raise ValueError("CAN ID必须是正整数")
        if can_type == 0:
            if can_id > 0x7FF:
                raise ValueError(f"标准帧ID必须在0-0x7FF(2047)范围内，当前ID: 0x{can_id:X}")
        elif can_type == 1:
            if can_id > 0x1FFFFFFF:
                raise ValueError(f"扩展帧ID必须在0-0x1FFFFFFF(536870911)范围内，当前ID: 0x{can_id:X}")
        else:
            raise ValueError("CAN类型必须是0(标准帧)或1(扩展帧)")
        if transmit_type not in (0, 2):
            raise ValueError("发送类型必须是0(正常发送)或2(自发自收)")
        if not isinstance(frame_count, int) or frame_count < 1:
            raise ValueError("发送帧数必须是正整数")
        if not isinstance(data_array, (list, tuple)):
            raise TypeError("数据参数必须是列表或元组")
        if is_canfd:
            if len(data_array) > 64:
                raise ValueError("CAN FD数据数组长度不能超过64")
        else:
            if len(data_array) > 8:
                raise ValueError("CAN 2.0数据数组长度不能超过8")
        for i, value in enumerate(data_array):
            if not isinstance(value, int):
                raise TypeError(f"第{i}个数据元素必须是整数")
            if value < 0 or value > 255:
                raise ValueError(f"第{i}个数据元素必须在0-255范围内")

        self.msg = CANMessage()
        self.msg.transmit_type = transmit_type
        self.msg.can_id = can_id
        self.msg.can_type = can_type
        self.msg.can_dlc = len(data_array)
        self.msg.data = list(data_array)
        self.msg.pad = 0x20
        self.msg.res0 = 10
        self.msg.res1 = 0
        self.msg.is_remote = is_remote
        self.msg.is_canfd = is_canfd
        self.frame_count = frame_count

        self.log_message(f"CAN消息对象已创建: {self.msg}")
        return self.msg

    def Transmit_msg(self, chn_handle):
        if not hasattr(self, 'msg'):
            raise ValueError("请先使用set_canmsg设置CAN消息参数")

        msgs = (ZCAN_Transmit_Data * self.frame_count)()

        for i in range(self.frame_count):
            msgs[i].transmit_type = self.msg.transmit_type
            msgs[i].frame.can_id = self.msg.can_id
            if self.msg.can_type == 1:
                msgs[i].frame.can_id |= 1 << 31
            if self.msg.is_remote:
                msgs[i].frame.can_id |= 1 << 30
            msgs[i].frame.can_dlc = self.msg.can_dlc
            msgs[i].frame._pad = self.msg.pad
            msgs[i].frame._res0 = self.msg.res0
            msgs[i].frame._res1 = self.msg.res1
            if not self.msg.is_remote:
                for j in range(self.msg.can_dlc):
                    msgs[i].frame.data[j] = self.msg.data[j]
            if self.msg.is_canfd:
                pass

        ret = zcanlib.Transmit(chn_handle, msgs, self.frame_count)
        self.log_message(f"已发送 {self.frame_count} 帧CAN消息，返回值: {ret}")
        return ret

    def open_device(self):
        if self.btn_OpenDev["text"] == "打开设备":
            self.handle = zcanlib.OpenDevice(ZCAN_USBCANFD_200U, 0, 0)
            if self.handle == INVALID_DEVICE_HANDLE:
                self.log_message("打开设备失败！")
            else:
                self.btn_OpenDev["text"] = "关闭设备"
                self.device_opened = True
                self.log_message(f"设备句柄: {self.handle}")
                self.can_number = self.Read_Device_Info(self.handle)
                self.Set_Device_Name(self.handle)

                for i in range(self.can_number):
                    chn_handle = self.USBCANFD_Start(zcanlib, self.handle, i)
                    if chn_handle is None:
                        self.log_message(f"启动通道{i}失败！")
                    else:
                        self.chn_handles.append(chn_handle)
                        self.log_message(f"通道句柄: {chn_handle}")

                if self.enable_merge_receive == 1:
                    thread = threading.Thread(target=self.receive_thread, args=(self.handle, self.chn_handles[0]))
                    self.threads.append(thread)
                    thread.start()
                else:
                    for i in range(len(self.chn_handles)):
                        thread = threading.Thread(target=self.receive_thread, args=(self.handle, self.chn_handles[i]))
                        self.threads.append(thread)
                        thread.start()
        else:
            self.btn_OpenDev["text"] = "打开设备"
            self.device_opened = False
            self.log_message("设备已关闭")

    def receive_thread(self, device_handle, chn_handle):
        CANType_width = len("CANFD加速    ")
        id_width = len(hex(0x1FFFFFFF))

        while self.thread_flag:
            time.sleep(0.005)
            rcv_num = zcanlib.GetReceiveNum(chn_handle, ZCAN_TYPE_CAN)
            if rcv_num:
                if rcv_num > 100:
                    rcv_msg, rcv_num = zcanlib.Receive(chn_handle, 100, 100)
                else:
                    rcv_msg, rcv_num = zcanlib.Receive(chn_handle, rcv_num, 100)
                with self.print_lock:
                    for msg in rcv_msg[:rcv_num]:
                        frame = msg.frame
                        direction = "TX" if frame._pad & 0x20 else "RX"
                        frame_type = "扩展帧" if frame.can_id & (1 << 31) else "标准帧"
                        frame_format = "远程帧" if frame.can_id & (1 << 30) else "数据帧"
                        can_id = frame.can_id & 0x1FFFFFFF

                        if frame.can_id & (1 << 30):
                            data = []
                            dlc = 0
                        else:
                            dlc = frame.can_dlc
                            data = [frame.data[j] for j in range(dlc)]

                        print(
                            f"[{msg.timestamp}] CAN{chn_handle & 0xFF} CAN\t{direction} ID: {hex(can_id):<{id_width}}\t{frame_type} {frame_format}"
                            f" DLC: {dlc}\tDATA(hex): {' '.join([f'{num:02X}' for num in data])}")

                        # 如果正在等待接收，将消息放入队列
                        if self.receive_event.is_set():
                            msg_dict = {
                                'can_id': can_id,
                                'can_type': 1 if (frame.can_id & (1 << 31)) else 0,
                                'data': data,
                                'timestamp': msg.timestamp,
                                'is_remote': bool(frame.can_id & (1 << 30)),
                                'dlc': dlc
                            }
                            try:
                                self.received_messages_queue.put_nowait(msg_dict)
                            except queue.Full:
                                try:
                                    self.received_messages_queue.get_nowait()
                                    self.received_messages_queue.put_nowait(msg_dict)
                                except queue.Empty:
                                    pass

            rcv_canfd_num = zcanlib.GetReceiveNum(chn_handle, ZCAN_TYPE_CANFD)
            if rcv_canfd_num:
                if rcv_num > 100:
                    rcv_canfd_msgs, rcv_canfd_num = zcanlib.ReceiveFD(chn_handle, 100, 100)
                else:
                    rcv_canfd_msgs, rcv_canfd_num = zcanlib.ReceiveFD(chn_handle, rcv_canfd_num, 100)
                with self.print_lock:
                    for msg in rcv_canfd_msgs[:rcv_canfd_num]:
                        frame = msg.frame
                        brs = "加速" if frame.flags & 0x1 else "   "
                        can_type = "CANFD" + brs
                        direction = "TX" if frame.flags & 0x20 else "RX"
                        frame_type = "扩展帧" if frame.can_id & (1 << 31) else "标准帧"
                        frame_format = "远程帧" if frame.can_id & (1 << 30) else "数据帧"
                        can_id = hex(frame.can_id & 0x1FFFFFFF)
                        data = " ".join([f"{num:02X}" for num in frame.data[:frame.len]])

                        print(
                            f"[{msg.timestamp}] CAN{chn_handle & 0xFF} {can_type:<{CANType_width}}\t{direction} ID: {can_id:<{id_width}}\t{frame_type} {frame_format}"
                            f" DLC: {frame.len}\tDATA(hex): {data}")

    # 设置滤波  白名单过滤(只接收范围内的数据)    USBCANFD设备支持设置最多64组滤波
    def Set_Filter(device_handle, chn):

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_clear", "0".encode("utf-8"))  # 清除滤波
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_clear failed!" % chn)
            return None

        # 流程为：for【set_mode(与前一组滤波同类型可以省略) + set_start + set_end】+ ack
        # 这里设置第一道滤波：标准帧0~0x7F，第二道滤波：标准帧0xFF~0x1FF，第三道滤波：扩展帧0xFF~0x2FF
        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_mode", "0".encode("utf-8"))  # 设置滤波模式 标准帧滤波
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_mode failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_start", "0".encode("utf-8"))  # 设置白名单范围 起始ID
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_start failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_end", "0x7F".encode("utf-8"))  # 设置白名单范围 结束ID
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_end failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_start", "0xFF".encode("utf-8"))
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_start failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_end", "0x1FF".encode("utf-8"))
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_end failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_mode", "1".encode("utf-8"))  # 扩展帧滤波
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_mode failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_start", "0xFF".encode("utf-8"))
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_start failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_end", "0x2FF".encode("utf-8"))
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_end failed!" % chn)
            return None

        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/filter_ack", "0".encode("utf-8"))  # 使能滤波
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  filter_ack failed!" % chn)
            return None

    # 启动通道
    def USBCANFD_Start(self, zcanlib, device_handle, chn):

        # 设置控制器设备类型，如果不是 Non-ISO CANFD 可注释掉按设备默认即可 CAN/CANFD 都是默认
        # ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/canfd_standard", "0".encode("utf-8"))

        # 仲裁域波特率 和 数据域波特率
        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/canfd_abit_baud_rate", "500000".encode("utf-8"))
        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/canfd_dbit_baud_rate", "2000000".encode("utf-8"))
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d baud failed!" % chn)
            return None

        # 自定义波特率    当产品波特率对采样点有要求，或者需要设置非常规波特率时使用   ---默认不管
        # ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/baud_rate_custom", "500Kbps(80%),2.0Mbps(80%),(80,07C00002,01C00002)".encode("utf-8"))
        # if ret != ZCAN_STATUS_OK:
        #     print("Set CH%d baud failed!" % chn)
        #     return None

        # 终端电阻
        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/initenal_resistance", "1".encode("utf-8"))
        if ret != ZCAN_STATUS_OK:
            print("Open CH%d resistance failed!" % chn)
            return None

        # 初始化通道
        chn_init_cfg = ZCAN_CHANNEL_INIT_CONFIG()
        chn_init_cfg.can_type = ZCAN_TYPE_CANFD  # USBCANFD 必须选择CANFD
        chn_init_cfg.config.canfd.mode = 0  # 0-正常模式 1-只听模式
        chn_handle = zcanlib.InitCAN(device_handle, chn, chn_init_cfg)
        if chn_handle is None:
            print("initCAN failed!" % chn)
            return None

        # 设置滤波
        # Set_Filter(device_handle, chn)

        # 设置发送回显    USBCANFD系列老卡（无LIN口，设备白色标签值版本为V1.02即以下,包括V1.02）需要以下操作，否则由CAN报文结构体_pad结构体bit5标识，见Transmit_Test 发送示例
        ret = zcanlib.ZCAN_SetValue(device_handle, str(chn) + "/set_device_tx_echo",
                                    "0".encode("utf-8"))  # 发送回显设置，0-禁用，1-开启
        if ret != ZCAN_STATUS_OK:
            print("Set CH%d  set_device_tx_echo failed!" % (chn))
            return None

        # 使能/关闭合并接收(startCAN 之前)    0-关闭 1-使能
        ret = zcanlib.ZCAN_SetValue(device_handle, str(0) + "/set_device_recv_merge",
                                    repr(self.enable_merge_receive))
        if ret != ZCAN_STATUS_OK:
            print("Open CH%d recv merge failed!" % chn)
            return None

        # 启动通道
        ret = zcanlib.StartCAN(chn_handle)
        if ret != ZCAN_STATUS_OK:
            print("startCAN failed!" % chn)
            return None

        return chn_handle

    # 读取设备信息
    def Read_Device_Info(self, handle):

        # 获取设备信息
        info = zcanlib.GetDeviceInf(handle)
        print("设备信息: \n%s" % info)

        can_number = info.can_num
        return can_number

    # 设置自定义序列号  ---用去区分同型号的CAN卡
    def Set_Device_Name(self, handle):
        ret = zcanlib.ZCAN_SetValue(handle, "0/set_cn", "A001".encode("utf-8"))
        if ret == ZCAN_STATUS_OK:
            t = zcanlib.ZCAN_GetValue(handle, "0/get_cn/1")
            print(c_char_p(t).value.decode("utf-8"))
        return

    def open_device(self):
        if self.btn_OpenDev["text"] == "打开设备":
            self.handle = zcanlib.OpenDevice(ZCAN_USBCANFD_200U, 0, 0)
            if self.handle == INVALID_DEVICE_HANDLE:
                self.log_message("打开设备失败！")
            else:
                self.btn_OpenDev["text"] = "关闭设备"
                self.device_opened = True
                self.log_message(f"设备句柄: {self.handle}")

                self.can_number = self.Read_Device_Info(self.handle)
                self.Set_Device_Name(self.handle)

                for i in range(self.can_number):
                    chn_handle = self.USBCANFD_Start(zcanlib, self.handle, i)
                    if chn_handle is None:
                        self.log_message(f"启动通道{i}失败！")
                    else:
                        self.chn_handles.append(chn_handle)
                        self.log_message(f"通道句柄: {chn_handle}")

                if self.enable_merge_receive == 1:
                    thread = threading.Thread(target=self.receive_thread, args=(self.handle, self.chn_handles[0]))
                    self.threads.append(thread)
                    thread.start()
                else:
                    for i in range(len(self.chn_handles)):
                        thread = threading.Thread(target=self.receive_thread,
                                                  args=(self.handle, self.chn_handles[i]))
                        self.threads.append(thread)
                        thread.start()
        else:
            # 关闭设备逻辑
            self.btn_OpenDev["text"] = "打开设备"
            self.device_opened = False
            self.log_message("设备已关闭")


# 主程序

root = tk.Tk()
zcanlib = ZCAN()

app = Application(master=root)
app.mainloop()
